#include <QBrush>
#include <QPen>
#include <QtGui>

#include <cmath>

#include "qGrid.h"

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

qGrid::qGrid()
{
}

qGrid::qGrid(QWidget *parent, Grid *grid)
    : QWidget(parent)
{
    mothership = grid;

    fillColor = 0xFF0000FF;

    resizeMode = 0;

    int x = mothership->CPscalefactor * mothership->geometry[0];
    int y = mothership->CPscalefactor * mothership->geometry[1];
    int w = mothership->CPscalefactor * mothership->geometry[2];
    int h = mothership->CPscalefactor * mothership->geometry[3];

    printf("parent width: %d\n", parent->width()); fflush(stdout);
    setGeometry(x, y, w, h);
}

void qGrid::highlightEdges(bool highlight)
{
    if (highlight)
    {
        for (int sx = 0; sx < mothership->gridSize[0]; sx++)
        {
            if (sx == 0 || sx == mothership->gridSize[0]-1) // For first and last cols, paint every square
            {
                for (int sy = 0; sy < mothership->gridSize[1]; sy++)
                {
                    mothership->squares[sx][sy] = 1;
                    mothership->colors[sx][sy] = 0x888888;
                }
            } else // fill just the top and bottom square
            {
                mothership->squares[sx][0] = 1;
                mothership->squares[sx][mothership->gridSize[1]-1] = 1;
                mothership->colors[sx][0] = 0x888888;
                mothership->colors[sx][mothership->gridSize[1]-1] = 0x888888;
            }
        }
    } else
    {
        for (int sx = 0; sx < mothership->gridSize[0]; sx++)
        {
            for (int sy = 0; sy < mothership->gridSize[1]; sy++)
            {
                mothership->squares[sx][sy] = 0;
                mothership->colors[sx][sy] = 0;
            }

        }
    }
    update();
}

void qGrid::clear()
{
    for (int sx = 0; sx < mothership->gridSize[0]; sx++)
    {
        for (int sy = 0; sy < mothership->gridSize[1]; sy++)
        {
            mothership->squares[sx][sy] = 0;
        }
    }

    update();
}

void qGrid::mousePressEvent(QMouseEvent *event)
{
    // If no modifier key, then turn the grid square on or off
    if (event->modifiers() == Qt::NoModifier)
    {
        if (event->button() == Qt::LeftButton)
        {
            int *clickedSquare = coord2square(event->x(), event->y());
            mothership->squares[clickedSquare[0]][clickedSquare[1]] = 1;
            printf("LMB: clicked square @ %d, %d\n", clickedSquare[0], clickedSquare[1]); fflush(stdout);
        }
        else if (event->button() == Qt::RightButton)
        {
            int *clickedSquare = coord2square(event->x(), event->y());
            mothership->squares[clickedSquare[0]][clickedSquare[1]] = 0;
            mothership->colors[clickedSquare[0]][clickedSquare[1]] = 0;
            printf("RMB: clicked square @ %d, %d\n", clickedSquare[0], clickedSquare[1]); fflush(stdout);
        }

    update();
    }

    // If Ctrl key down, set the resizeMode
    else if (event->modifiers() == Qt::ControlModifier)
    {
        // Save the mouse position when the button was pressed
        oldMousePos = mapToParent(event->pos());
        oldGridPos = pos();
        oldWidth = width();
        oldHeight = height();
        printf("pos: %d, %d", pos().x(), pos().y()); fflush(stdout);
        
        // Find out if we're within +/- 2 of an edge
        if (oldMousePos.x() <= oldGridPos.x()+10 &&
            oldMousePos.x() >= oldGridPos.x()-10)
        {
            resizeMode ^= 0x1;
            printf("left "); fflush(stdout);
        }
        if (oldMousePos.y() <= oldGridPos.y()+10 &&
            oldMousePos.y() >= oldGridPos.y()-10)
        {
            resizeMode ^= 0x2;
            printf("top "); fflush(stdout);
        }
        if (oldMousePos.x() <= oldGridPos.x()+width()+10 &&
            oldMousePos.x() >= oldGridPos.x()+width()-10)
        {
            resizeMode ^= 0x4;
            printf("right "); fflush(stdout);
        }
        if (oldMousePos.y() <= oldGridPos.y()+height()+10 &&
            oldMousePos.y() >= oldGridPos.y()+height()-10)
        {
            resizeMode ^= 0x8;
            printf("bot "); fflush(stdout);
        }
        printf("\nMouse: %d, %d\nGrid: %d, %d\n", oldMousePos.x(), oldMousePos.y(),
                                                  oldGridPos.x(), oldGridPos.y()); fflush(stdout);
        printf("%d\n", resizeMode); fflush(stdout);
    }
}

void qGrid::mouseMoveEvent(QMouseEvent *event)
{
    if (event->modifiers() == Qt::ControlModifier)
    {
        QPoint newMousePos = mapToParent(event->pos());
        QPoint delta = newMousePos - oldMousePos;
        int squaresX = (float(delta.x()) / mothership->gridWidth) + sgn(delta.x())*0.5;
        int squaresY = (float(delta.y()) / mothership->gridWidth) + sgn(delta.y())*0.5;

        printf("delta: %d %d ", delta.x(), delta.y()); fflush(stdout);
        QPoint newPos;
        QSize newSize;

        printf("squares: %d, %d\n", squaresX, squaresY); fflush(stdout);

        if (resizeMode == 0)
        {
            // No edge (move the grid instead of resizing)
            move(oldGridPos+delta);
        }
        else // if ((delta.x() % mothership->gridWidth == 0) || (delta.y() % mothership->gridWidth == 0))
        {
            switch (resizeMode)
            {
                case 0x1: // Left edge
                    newPos = QPoint( oldGridPos.x() + squaresX*mothership->gridWidth, oldGridPos.y() );
                    newSize = QSize( oldWidth - squaresX*mothership->gridWidth, oldHeight );
                    setGeometry(QRect( newPos,  newSize));
                    break;
                case 0x2: // Top edge
                    newPos = QPoint( oldGridPos.x(), oldGridPos.y() + squaresY*mothership->gridWidth );
                    newSize = QSize( oldWidth, oldHeight - squaresY*mothership->gridWidth );
                    setGeometry(QRect( newPos,  newSize));
                    break;
                case 0x3: // Top left corner
                    newPos = QPoint( oldGridPos.x() + squaresX*mothership->gridWidth, oldGridPos.y() + squaresY*mothership->gridWidth );
                    newSize = QSize( oldWidth - squaresX*mothership->gridWidth, oldHeight - squaresY*mothership->gridWidth );
                    setGeometry(QRect( newPos,  newSize));
                    break;
                case 0x4: // Right edge
                    newPos = oldGridPos;
                    newSize = QSize( oldWidth + squaresX*mothership->gridWidth, oldHeight );
                    setGeometry(QRect( newPos,  newSize));
                    break;
                case 0x6: // Top right corner
                    newPos = QPoint( oldGridPos.x(), oldGridPos.y() + squaresY*mothership->gridWidth );
                    newSize = QSize( oldWidth + squaresX*mothership->gridWidth, oldHeight - squaresY*mothership->gridWidth );
                    setGeometry(QRect( newPos,  newSize));
                    break;
                case 0x8: // Bot edge
                    newPos = oldGridPos;
                    newSize = QSize( oldWidth, oldHeight + squaresY*mothership->gridWidth );
                    setGeometry(QRect( newPos,  newSize));
                    break;
                case 0x9: // Bot left corner
                    newPos = QPoint( oldGridPos.x() + squaresX*mothership->gridWidth, oldGridPos.y() );
                    newSize = QSize( oldWidth - squaresX*mothership->gridWidth, oldHeight + squaresY*mothership->gridWidth );
                    setGeometry(QRect( newPos,  newSize));
                    break;
                case 0xC: // Bot right corner
                    newPos = oldGridPos;
                    newSize = QSize( oldWidth + squaresX*mothership->gridWidth, oldHeight + squaresY*mothership->gridWidth );
                    setGeometry(QRect( newPos,  newSize));
                    break;
                default:
                    printf("Resize mode %d invalid\n", resizeMode); fflush(stdout);
                    break;
            }
        }

        // Phone home to the mothership
        mothership->geometry[0] = float(pos().x()) / mothership->CPscalefactor;
        mothership->geometry[1] = float(pos().y()) / mothership->CPscalefactor;
        mothership->geometry[2] = float(  width()) / mothership->CPscalefactor;
        mothership->geometry[3] = float( height()) / mothership->CPscalefactor;

        if (resizeMode != 0) { mothership->updateSquares(); }
        emit gridMove();
    }
}

void qGrid::mouseReleaseEvent(QMouseEvent * /*event*/)
{
    resizeMode = 0;
}

void qGrid::paintEvent(QPaintEvent * /*event*/)
{
    int x = mothership->CPscalefactor * mothership->geometry[0];
    int y = mothership->CPscalefactor * mothership->geometry[1];
    int w = mothership->CPscalefactor * mothership->geometry[2];
    int h = mothership->CPscalefactor * mothership->geometry[3];

    setGeometry(x, y, w, h);

    QPainter p(this);
    QPen pen(QColor(0, 160, 255, 145));
    pen.setStyle(Qt::DotLine);
    p.setRenderHints(QPainter::Antialiasing);
    p.setPen(pen);

    // Draw a grid
    for (float x = 0.0; x <= width(); x += (mothership->CPscalefactor * mothership->gridWidth))
    {
        QPointF p1(x, 0);
        QPointF p2(x, height());
        p.drawLine(p1, p2);
    }

    for (float y = 0.0; y <= height(); y += (mothership->CPscalefactor * mothership->gridWidth))
    {
        QPointF p1(0, y);
        QPointF p2(width(), y);
        p.drawLine(p1, p2);
    }

    // Draw the last lines in the grid
    p.drawLine(width()-1, 0 , width()-1, height()-1);
    p.drawLine(0, height()-1, width()-1, height()-1);

    // Paint the active squares
    p.setPen(QPen(Qt::NoPen));
    for (int sx = 0; sx < mothership->gridSize[0]; sx++)
    {
        for (int sy = 0; sy < mothership->gridSize[1]; sy++)
        {
            if (mothership->squares[sx][sy] != 0)
            {
                QColor color(fillColor);
                QBrush brush(QColor(color), Qt::SolidPattern);
                p.setBrush(brush);

                QRectF square(mothership->CPscalefactor * sx * mothership->gridWidth,
                              mothership->CPscalefactor * sy * mothership->gridWidth,
                              mothership->CPscalefactor * mothership->gridWidth - 1,
                              mothership->CPscalefactor * mothership->gridWidth - 1);
                p.drawRect(square);
            }
        }
    }

}

int* qGrid::coord2square(int x, int y)
{
    static int square[2];
    square[0] = int(x / (mothership->CPscalefactor * mothership->gridWidth));
    square[1] = int(y / (mothership->CPscalefactor * mothership->gridWidth));
    
    return square;
}
